package craft;

/**
 * 实现一个计算器，但计算的结合性是有问题的。因为它使用了下面的语法规则：
 *
 * additive -> multiplicative | multiplicative + additive
 * multiplicative -> primary | primary * multiplicative    //感谢@Void_seT，原来写成+号了，写错了。
 *
 * 递归项在右边，会自然的对应右结合。我们真正需要的是左结合。
 */

public class SimpleCalculator {

    public static void main(String[] args) {
        SimpleCalculator calculator = new SimpleCalculator();

        //测试变量声明语句的解析
        String script = "int a = b+3;";
        System.out.println("解析变量声明语句: " + script);
        SimpleLexer lexer = new SimpleLexer();
        TokenReader tokens = lexer.tokenize(script);
        try {
            SimpleASTNode node = calculator.intDeclare(tokens);
            calculator.dumpAST(node,"");
        }
        catch (Exception e){
            System.out.println(e.getMessage());
        }

        //测试表达式
        script = "2+3*5";
        System.out.println("\n计算: " + script + "，看上去一切正常。");
        calculator.evaluate(script);

        //测试语法错误
        script = "2+";
        System.out.println("\n: " + script + "，应该有语法错误。");
        calculator.evaluate(script);

        script = "2+3+4";
        System.out.println("\n计算: " + script + "，结合性出现错误。");
        calculator.evaluate(script);
    }

    /**
     * 执行脚本，并打印AST和求值过程
     * @param script
     */
    private void evaluate(String script) {
        try {
            ASTNode tree = parse(script);
            dumpAST(tree, "");
            evaluate(tree, "");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 对某个AST节点求值，并打印求值过程
     * @param node
     * @param indent
     */
    private int evaluate(ASTNode node, String indent) {
        int result = 0;

        System.out.println(indent + "Calculating: " + node.getType());

        switch(node.getType()) {
            case Program:
                for (ASTNode child : node.getChildren()) {
                    result = evaluate(child, indent + "\t");
                }
                break;
            case Additive:
                SimpleASTNode child1 = (SimpleASTNode) node.getChildren().get(0);
                int value1 = evaluate(child1, indent + "\t");
                SimpleASTNode child2 = (SimpleASTNode) node.getChildren().get(1);
                int value2 = evaluate(child2, indent + "\t");
                if (node.getText().equals("+")) {
                    result = value1 + value2;
                } else if (node.getText().equals("-")) {
                    result = value1 - value2;
                }
                break;
            case Multiplicative:
                child1 = (SimpleASTNode) node.getChildren().get(0);
                value1 = evaluate(child1, indent + "\t");
                child2 = (SimpleASTNode) node.getChildren().get(1);
                value2 = evaluate(child2, indent + "\t");
                if (node.getText().equals("*")) {
                    result = value1 * value2;
                } else if (node.getText().equals("/")) {
                    result = value1 / value2;
                }
                break;
            case IntDeclaration:
                result = Integer.valueOf(node.getText()).intValue();
        }

        System.out.println(indent + "Result: " + result);

        return result;
    }

    /**
     * 解析脚本，并返回根节点
     * @param script
     * @return
     */
    private ASTNode parse(String script) throws Exception {
        SimpleLexer lexer = new SimpleLexer();
        TokenReader tokens = lexer.tokenize(script);

        ASTNode node = prog(tokens);
        return node;
    }

    /**
     * 解析token流，构建AST，并返回根节点
     * @param tokens
     * @return
     */
    private ASTNode prog(TokenReader tokens) throws Exception {
        SimpleASTNode node = new SimpleASTNode(ASTNodeType.Program, "Calculator");

        SimpleASTNode child = additive(tokens);

        if (child != null) {
            node.addChild(child);
        }

        return node;
    }

    /**
     * 解析是否满足整形变量声明语句
     * 如：int a;
     *     int b = 2 * 3;
     *
     * @param tokens
     * @return
     */
    private SimpleASTNode intDeclare(TokenReader tokens) throws Exception {
        // 先预读一个token
        Token token = tokens.peek();
        SimpleASTNode node = null;
        // 尝试匹配int
        if (token != null && token.getType() == TokenType.Int) {
            // 消耗掉int
           tokens.read();
           token = tokens.peek();
           // 尝试匹配标识符
           if (token != null && token.getType() == TokenType.Identifier) {
               // 消耗掉标识符
               tokens.read();
               // 创建节点，并保存变量名到当前节点的文本值中
               node = new SimpleASTNode(ASTNodeType.IntDeclaration, token.getText());
               token = tokens.peek();
               if (token != null && token.getType() == TokenType.Assignment) {
                   // 消耗掉 赋值等号
                   token = tokens.read();
                   SimpleASTNode child = additive(tokens);      // 匹配表达式
                   if (child == null) {
                       throw new Exception("Invalid variable initialization, expecting an expression");
                   } else {
                       node.addChild(child);
                   }
               }
           } else {
               throw new Exception("variable name expected");
           }

           if (node != null) {
               token = tokens.peek();
               if (token != null && token.getType() == TokenType.SemiColon) {
                   // 消耗 逗号
                   tokens.read();
               } else {
                   throw new Exception("Invalid statement, expecting semicolon");
               }
           }
        }
        return node;
    }

    /**
     * 匹配 加法表达式
     * 这里 加减本质一样，乘除本质一样
     * additive -> multiplicative | multiplicative + additive
     * multiplicative -> primary | primary * multiplicative    //感谢@Void_seT，原来写成+号了，写错了。
     * @param tokens
     * @return
     */
    private SimpleASTNode additive(TokenReader tokens) throws Exception {
        SimpleASTNode child1 = multiplicative(tokens);
        SimpleASTNode node = child1;

        Token token = tokens.peek();
        if (child1 != null && token != null) {
            // 尝试 匹配 加号 或者 减号
            if (token.getType() == TokenType.Plus || token.getType() == TokenType.Minus) {
                 // 消耗 加号 或者 减号
                token = tokens.read();
                SimpleASTNode child2 = additive(tokens);
                if (child2 != null) {
                    node = new SimpleASTNode(ASTNodeType.Additive, token.getText());
                    node.addChild(child1);
                    node.addChild(child2);
                } else {
                    throw new Exception("invalid additive expression, expecting the right part.");
                }
            }
        }
        return node;
    }

    /**
     * 匹配 乘法表达式
     * multiplicative -> primary | primary * multiplicative
     * @param tokens
     * @return
     */
    private SimpleASTNode multiplicative(TokenReader tokens) throws Exception {
        SimpleASTNode child1 = primary(tokens);
        SimpleASTNode node = child1;

        Token token = tokens.peek();
        if (child1 != null && token != null) {
            // 尝试匹配 乘号 或者 除号
            if (token.getType() == TokenType.Star || token.getType() == TokenType.Slash) {
                // 消耗 乘号 或者 除号
                tokens.read();
                SimpleASTNode child2 = multiplicative(tokens);
                if (child2 != null) {
                    node = new SimpleASTNode(ASTNodeType.Multiplicative, token.getText());
                    node.addChild(child1);
                    node.addChild(child2);
                } else {
                    throw new Exception("invalid multiplicative expression, expecting the right part.");
                }
            }
        }
        return node;
    }

    /**
     * 匹配 基础表达式
     * primary -> IntLiteral | Identifier | '(' additive ')'
     * @param tokens
     * @return
     */
    private SimpleASTNode primary(TokenReader tokens) throws Exception {
        SimpleASTNode node = null;
        Token token = tokens.peek();
        if (token != null && token.getType() == TokenType.IntLiteral) {
            tokens.read();
            node = new SimpleASTNode(ASTNodeType.IntLiteral, token.getText());
        } else if (token != null && token.getType() == TokenType.Identifier) {
           tokens.read();
           node = new SimpleASTNode(ASTNodeType.Identifier, token.getText());
        } else if (token != null && token.getType() == TokenType.LeftParen) {
           tokens.read();
           node = additive(tokens);
           if (node != null) {
               token = tokens.read();
               if (token != null && token.getType() == TokenType.RightParen) {
                   tokens.read();
               } else {
                   throw new Exception("expecting right parenthesis");
               }
           } else {
               throw new Exception("expecting an additive expression inside parenthesis");
           }
        }
        return node;
    }

    /**
     * 打印AST的所有节点
     * @param node
     * @param indent：指定缩进字符，由tab组成，每一级多一个tab
     */
    public void dumpAST(ASTNode node, String indent) {
        System.out.println(indent + node.getType() + " " + node.getText());
        for (ASTNode child : node.getChildren()) {
            dumpAST(child, indent);
        }
    }
}
